package com.zgkxzx.myfix;

import android.content.Context;
import android.util.Log;


import com.alipay.euler.andfix.annotation.MethodReplace;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.Enumeration;
import java.util.List;

import dalvik.system.DexFile;

/**
 * 创建者 zgkxzx
 * 日期   1/30/18
 * 描述
 */

public class AndFixManager {
    private final static String TAG = AndFixManager.class.getSimpleName();

    private Context context;

    private File newFile;



    public AndFixManager(Context context, int appVersion) {
        this.context = context;
        HandleNative.init(appVersion);
    }

    public void fix(File srcFile, ClassLoader classLoader, List<String> lists) {
        Log.d(TAG, "fix srcFile = " + srcFile.getAbsolutePath());
        newFile = new File(context.getCacheDir(), srcFile.getName());

        try {
            //通过DexFile载入dex文件
            final DexFile dexFile = DexFile.loadDex(srcFile.getAbsolutePath(), newFile.getAbsolutePath(), Context.MODE_PRIVATE);

            //通过先前配置文件的需要替换的文件，将需要替换的重现载入
            Enumeration<String> entries = dexFile.entries();
            while (entries.hasMoreElements()) {
                String key = entries.nextElement();
                if (!lists.contains(key)) {
                    continue;
                }
                Class realClass = dexFile.loadClass(key, new ClassLoader() {
                    @Override
                    public Class<?> loadClass(String name) throws ClassNotFoundException {
                        //return super.loadClass(name);
                        Class aClass = dexFile.loadClass(name, this);
                        if (aClass == null)
                            aClass = Class.forName(name);
                        return aClass;
                    }
                });
                if (realClass != null) {
                    fixClass(realClass, classLoader);
                }
            }

        } catch (IOException e) {
            e.printStackTrace();
        }


    }

    //通过注解查找与替换
    private void fixClass(Class realClass, ClassLoader classLoader) {
        Log.d(TAG, "fixClass = " + realClass.getSimpleName());
        Method[] methods = realClass.getDeclaredMethods();
        Log.d(TAG, "methods  = " + methods.toString());
        for (Method method : methods) {
            Log.d(TAG, "method  = " + method.toString());
            MethodReplace methodReplace = method.getAnnotation(MethodReplace.class);
            if (methodReplace == null) {
                continue;
            }
            String clazz = methodReplace.clazz();
            String method1 = methodReplace.method();
            replaceMethod(classLoader, clazz, method1,  method);
            //replaceMethod(classLoader,clazz,method1, method);
        }

    }

    //替换
    private void replaceMethod(ClassLoader classLoader, String clz, String method1, Method srcMethod) {
        Log.d(TAG, "replaceMethod clazz = " + clz + " method1 = " + method1);
        try {
            Class<?> newClass = Class.forName(clz);

            Method newMethod = newClass.getDeclaredMethod(method1, srcMethod.getParameterTypes());

            HandleNative.replaceMethod(newMethod, srcMethod);

        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }


    }


}


/*
public static DexFile loadDex (String sourcePathName, String outputPathName, int flags)

打开一个DEX文件，并提供一个文件来保存优化过的DEX数据。如果优化过的格式已存在并且是最新的，就直接使用它。如果不是，虚拟机将试图重新创建一个。该方法主要用于应用希望在通常的应用安装机制之外下载和执行DEX文件。不能在应用里直接调用该方法，而应该通过一个类装载器例如dalvik.system.DexClassLoader.

    参数

        sourcePathName     包含”classes.dex”的Jar或者APK文件。(将来可能会扩展支持"raw DEX"。)

        outputPathName     保存优化过的DEX数据的文件。

        flags        打开可选功能（目前什么也没定义）

    返回值

        一个新的，或者先前已经打开的DexFile。

    异常

        IOException     无法打开输入或输出文件。


public Enumeration<String> entries ()

枚举DEX文件里面的类名。

    返回值

        DEX文件所包含类名的枚举，类名的类型是一般内部格式（像java/lang/String）。
*/